package com.study.principle;

/**
 * 6大设计原则：开闭原则
 *
 * 应该通过扩展来实现变化（对扩展开放），而不是通过修改已有的代码来实现变化（对修改关闭）；
 * 设计一个软件系统模块（类，方法）的时候，应该在不修改原有的模块（修改关闭）的基础上扩展其功能（扩展开放）；
 *
 * 优势在于软件系统可复用，并且易于维护：
 *    * 稳定性：扩展功能不修改原来的代码，这可以让软件系统在变化中保持稳定。
 *    * 扩展性：对扩展开放，通过扩展提供新的或改变原有的功能，让软件系统具有灵活的可扩展性。
 *
 * @Author 网易云课堂微专业-java高级开发工程师【allen老师】
 * @Version 1.0
 */
public class OpenClosedPrinciple {
    public static void main(String[] args) {
        IBook novel = new NovelBook("富婆联系方式", 100, "Allen");
        System.out.println(
                "书籍名字：" + novel.getName() +"\t"+
                "书籍作者：" + novel.getAuthor() +"\t"+
                "书籍价格：" + novel.getPrice()
        );

        IBook offNovel = new OffNovelBook("大宝剑攻略", 100, "Allen");
        System.out.println(
                "书籍名字：" + offNovel.getName() +"\t"+
                "书籍作者：" + offNovel.getAuthor() +"\t"+
                "书籍价格：" + offNovel.getPrice()
        );
//
//        IBook offNove2 = new OffNovelBook("好好学习，天天向上", 30, "Allen");
//        System.out.println(
//                "书籍名字：" + offNove2.getName() +"\t"+
//                        "书籍作者：" + offNove2.getAuthor() +"\t"+
//                        "书籍价格：" + offNove2.getPrice()
//        );
    }
}

// 书籍接口
interface IBook {
    public String getName();
    public double getPrice();
    public String getAuthor();
}

// 小说类书籍
class NovelBook implements IBook {
    private String name;
    private double price;
    private String author;

    public NovelBook(String name, int price, String author) {
        this.name = name;
        this.price = price;
        this.author = author;
    }

    @Override
    public String getAuthor() {
        return this.author;
    }

    @Override
    public String getName() {
        return this.name;
    }
    @Override
    public double getPrice() {
        return this.price;
    }
}

/*
项目投入使用后，书籍正常销售，但是我们经常因为各种原因，要打折来销售书籍，这是一个变化，我们要如何应对这样一个需求变化呢？
我们有下面三种方法可以解决此问题：

1、修改接口
在IBook接口中，增加一个方法getOffPrice(),专门用于进行打折处理，所有的实现类实现此方法。
但是对于这样的一个修改方式，首先，作为接口，IBook应该稳定且可靠，不应该经常发生改变，否则接口作为契约的作用就失去了。
其次，并不是所有的书籍都需要打折销售，仅仅因为NovelBook打折销售就修改接口使所有书都必须实现打折销售的逻辑，显然与实际业务不符。因此，此方案否定。

2、修改实现类
修改NovelBook类的方法，直接在getPrice()方法中实现打折处理。此方法是有问题的，例如我们如果getPrice()方法中只需要读取书籍的打折前的价格呢？这不是有问题吗？当然我们也可以再增加getOffPrice()方法，这也是可以实现其需求，但是这就有二个读取价格的方法，因此，该方案也不是一个最优方案。

3、通过扩展实现变化
我们可以增加一个子类OffNovelBook（继承自NovelBook）,覆写getPrice()方法。此方法修改少，对现有的代码没有影响，风险少，是最好的办法，同时也符合开闭原则。
 */
class OffNovelBook extends NovelBook {

    public OffNovelBook(String name, int price, String author) {
        super(name, price, author);
    }

    //覆写价格方法，当价格大于40，就打8析，其他价格就打9析
    @Override
    public double getPrice() {
        if (super.getPrice() > 40) {
            return super.getPrice() * 0.8;
        } else {
            return super.getPrice() * 0.9;
        }
    }
}